import threading
import time
from typing import Union, Callable, Any, Dict, List

# 事件类型定义
EVENT_TYPES = {
    "use": "use",   # 物品使用
    "trigger": "trigger",   # buff触发
    "on_trigger": "on_trigger",
    "on_use": "on_use",
    "on_hit": "on_hit",
    "on_damage": "on_damage",
    "on_attack": "on_attack",   # 被武器攻击
    "on_kill": "on_kill",
    "on_revive": "on_revive",
    "on_start": "on_start",  # 战斗开始事件
    "on_stamina_change": "on_stamina_change",  # 新增：耐力变化事件
}

# 物品类别定义
ITEM_TYPE = {
    "buff": "buff",
    "animal": "animal",
    "none": "none"
}

ATTRIBUTE_TYPES = {
    "light": "light",
    "nature": "nature",
    "dark": "dark",
    "none": "none",
}

BUFF_TYPE = {
    "heart": "heart",
    "spike": "spike",
    "mp": "mp"
}


def id_generation(start=1):
    current = start
    while True:
        yield current
        current += 1


# 创建生成器对象
id_generator = id_generation()


# 计时器
class Timer:
    def __init__(self, interval=0.01):
        self._interval = interval
        self._running = False
        self._handlers = []
        self._thread = None
        self._real_start_time = None
        self._speed = 1

    @property
    def time(self) -> float:
        if not self._running:
            return 0.0
        else:
            real_interval = time.perf_counter() - self._real_start_time
            return real_interval * self._speed

    @property
    def speed(self) -> float:
        return self._speed

    @speed.setter
    def speed(self, value: float) -> None:
        if value <= 0:
            raise ValueError(f"speed must >= 0!")
        self._speed = value

    @property
    def is_running(self) -> bool:
        return self._running

    def start(self):
        """启动计时器"""
        if self._running:
            return

        self._real_start_time = time.perf_counter()
        self._running = True
        self._thread = threading.Thread(target=self._loop, daemon=True)
        self._thread.start()

    def stop(self):
        """停止计时器"""
        self._running = False

    def pause(self):
        """暂停计时器"""
        self._running = False

    def add_handler(self, handler):
        """
        添加事件处理函数
        :param handler: 函数，接受零个或多个参数
        """
        if callable(handler):
            self._handlers.append(handler)
        else:
            raise ValueError(f"Handler {handler} must be callable.")

    def _loop(self):
        """定时执行事件检查"""
        next_time = time.perf_counter()
        while self._running:
            for handle in self._handlers:
                handle()

            # 控制下一次执行时间
            next_time += self._interval
            now = time.perf_counter()
            if next_time > now:
                time.sleep(next_time - now)
            else:
                # 如果处理时间超过了间隔，跳过 sleep
                next_time = now

            # 超时退出
            if self.time > 500:
                print("超时！停止计时器")
                self.stop()


timer = Timer()


# 事件系统
class EventSystem:
    def __init__(self):
        # handlers 结构：{event_type: [dict]}
        self._handlers: Dict[str, List[dict]] = {}

    def register(self, event_type: str, source: Any, callback: Callable, *args, **kwargs):
        """
        注册事件监听器
        :param source: 事件注册者
        :param event_type: 事件类型
        :param callback: 回调函数
        """
        if event_type not in self._handlers:
            self._handlers[event_type] = []

        self._handlers[event_type].append(
            {'callback': callback,
             'source': source}
        )

    # def unregister(self, event_type: str, callback: Callable):
    #     """
    #     移除事件监听器
    #     :param event_type: 事件类型
    #     :param callback: 要移除的回调函数
    #     """
    #     if event_type in self._handlers and callback in self._handlers[event_type]:
    #         self._handlers[event_type].remove(callback)

    def emit(self, event_type: str, *args, **kwargs):
        """
        触发事件，调用所有绑定的回调函数
        :param event_type: 事件类型
        :param args: 位置参数
        :param kwargs: 关键字参数
        """
        if event_type not in self._handlers:
            return
        # 触发战斗开始事件
        if event_type == EVENT_TYPES['on_start']:
            self._emit_battle_start(*args, **kwargs)
        else:
            self._emit_default(event_type, *args, **kwargs)

    # def clear(self, event_type: Optional[str] = None):
    #     """
    #     清除所有或指定类型的事件监听器
    #     :param event_type: 如果为 None，则清除所有监听器
    #     """
    #     if event_type is None:
    #         self._handlers.clear()
    #     elif event_type in self._handlers:
    #         self._handlers[event_type].clear()
    #
    def has_handler(self, event_type: str, callback: Callable = None) -> bool:
        """
        检查是否有绑定的回调函数
        :param event_type: 事件类型
        :param callback: 如果为 None，则检查是否有任意回调
        :return: bool
        """
        if event_type not in self._handlers:
            return False
        if callback is None:
            return len(self._handlers[event_type]) > 0
        return callback in self._handlers[event_type]
    #
    # def get_handlers(self, event_type: str) -> List[Callable]:
    #     """
    #     获取某个事件类型的所有回调函数
    #     """
    #     return self._handlers.get(event_type, [])

    def _emit_default(self, event_type: str, *args, **kwargs):
        for event_dict in self._handlers[event_type]:
            source = event_dict['source']
            event_dict['callback'](source=source, *args, **kwargs)

    def _emit_battle_start(self, *args, **kwargs):
        event_type = EVENT_TYPES['on_start']
        print(f"战斗开始时，触发一些物品效果")

        # 本质是触发一遍带有战斗开始物品的on_use事件
        for event_dict in self._handlers[event_type]:
            source = event_dict['source']
            callback = event_dict['callback']

            # 判断事件来源，如果不为item，暂时报错，待扩展
            if type(source) is not Item:
                raise TypeError(f"{type(source)} is wrong source type")

            item = source
            # 触发事件
            callback(item=item, time=timer.time, *args, **kwargs)
            self.emit(EVENT_TYPES['on_use'], time=timer.time)


event_system = EventSystem()


# 战场系统
class BattleSystem:
    def __init__(self):
        pass


# 物品类
class Item:
    def __init__(self, index: int, name: str, item_type: str = ITEM_TYPE["none"], attributes: Union[list | None] = None,
                 cooldown: Union[float | None] = None,
                 callbacks: dict = None,
                 text: str = None,
                 description: str = None,
                 is_use_once: Union[bool | None] = None):
        self._id = index
        self._name = name
        self._type = item_type
        self._attributes = attributes or []
        self._base_cooldown = cooldown  # 基础冷却时间
        self._current_cooldown = cooldown  # 当前冷却时间
        self._last_used = 0
        self._callbacks = callbacks or {}  # {event_type: [callbacks]}
        self._text = text or ""
        self._description = description or ""
        self._is_use_once = is_use_once

        self._owner = None
        self._opponent = None
        self._use_count = 0

    @property
    def id(self) -> int:
        return self._id

    @property
    def name(self) -> str:
        return self._name

    @property
    def type(self):
        return self._type

    @property
    def attributes(self) -> list:
        return self._attributes

    @property
    def base_cooldown(self) -> float:
        return self._base_cooldown

    @property
    def last_used(self) -> float:
        return self._last_used

    @property
    def owner(self) -> 'Character':
        return self._owner

    @property
    def opponent(self) -> 'Character':
        return self._opponent

    @property
    def callbacks(self) -> dict:
        return self._callbacks

    def can_use(self, current_time) -> bool:
        # 没有冷却时间
        if self._current_cooldown is None:
            return False
        else:
            return current_time - self._last_used >= self._current_cooldown

    def use(self, current_time, *args, **kwargs):
        self._use_count += 1  # 使用次数加1
        event_system.emit(EVENT_TYPES["on_use"], item=self, time=current_time)  # 触发回调事件
        self._last_used = current_time  # 记录最后一次使用时间

        # 触发使用效果函数
        for callback in self.get_callbacks_by_event_type(EVENT_TYPES['use']):
            callback(item=self, time=current_time, *args, **kwargs)

        # 如果这个物品只能使用一次，将冷却时间设置为None
        if self._is_use_once:
            self._current_cooldown = None

    def set_owner(self, owner: 'Character') -> None:
        self._owner = owner

    def set_opponent(self, opponent: 'Character') -> None:
        self._opponent = opponent

    def get_callbacks_by_event_type(self, event_type: str) -> list:
        for _type, callbacks in self._callbacks.items():
            if event_type == _type:
                return callbacks
        else:
            return []


class Weapon(Item):
    def __init__(self, index: int, name: str, attack: float, stamina: float, *args, **kwargs):
        super().__init__(index, name, *args, **kwargs)
        self._attack = attack
        self._stamina = stamina  # 耐力消耗

    @property
    def attack(self) -> float:
        return self._attack

    @attack.setter
    def attack(self, value: float) -> None:
        self._attack = value

    @property
    def stamina(self) -> float:
        return self._stamina

    def can_use(self, current_time) -> bool:
        boolean = super().can_use(current_time)
        # 武器需要额外判断耐力值是否达标
        # 如果冷却时间到并且耐力达标， 可使用
        if self.owner.stamina >= self.stamina and boolean:
            return True
        # 如果冷却时间到但耐力不达标，不可使用，更新本次的使用时间
        elif self.owner.stamina < self.stamina and boolean:
            self._last_used = current_time
            return False
        else:
            return False

    def use(self, current_time, *args, **kwargs):
        super().use(current_time, *args, **kwargs)

        # 武器需要额外减掉使用的耐力值
        self.owner.stamina -= self.stamina

        # 武器的固定动作， 攻击并造成伤害
        attack_action(item=self, time=current_time, damage=self.attack)


class Buff:
    def __init__(self, buf_type, cooldown=None, callbacks=None, stack=1, *args, **kwargs):
        self._name = buf_type
        self._type = buf_type
        self._base_cooldown = cooldown  # 基础冷却时间
        self._current_cooldown = cooldown  # 当前冷却时间
        self._last_used = 0
        self._callbacks = callbacks or {}
        self._stack = stack
        self._owner = None

    @property
    def name(self) -> str:
        return self._name

    @property
    def type(self) -> str:
        return self._type

    @property
    def current_cooldown(self) -> float:
        return self._current_cooldown

    @property
    def stack(self) -> int:
        return self._stack

    @stack.setter
    def stack(self, value: int) -> None:
        if value < 0:
            value = 0
        self._stack = value

    @property
    def owner(self) -> 'Character':
        return self._owner

    @property
    def last_used(self) -> float:
        return self._last_used

    @last_used.setter
    def last_used(self, value: float) -> None:
        self._last_used = value

    def can_trigger(self, current_time) -> bool:
        # 没有层数
        if self._stack == 0:
            return False
        # 没有冷却时间
        if self._current_cooldown is None:
            return False
        else:
            return current_time - self._last_used >= self._current_cooldown

    def trigger(self, cur_time: float, *args, **kwargs):
        print(f"{self.name}被触发")

    def set_owner(self, character: 'Character') -> None:
        self._owner = character


class Heart(Buff):
    def __init__(self, *args, **kwargs):
        super().__init__(BUFF_TYPE['heart'], 2, *args, **kwargs)

    def trigger(self, cur_time: float, *args, **kwargs):
        self.owner.hp = self.owner.hp + self.stack
        self._last_used = cur_time
        print(
            f"[{cur_time:.2f}s] {self.name}被触发，{self.owner.name}增加生命值{self.stack}点, 当前生命值为{self.owner.hp}/{self.owner.max_hp}")


class Spike(Buff):
    def __init__(self, *args, **kwargs):
        super().__init__(BUFF_TYPE['spike'], *args, **kwargs)

    def trigger(self, damage: float, cur_time: float = None, *args, **kwargs):
        # 如果没有层数，不触发
        if self.stack == 0:
            return

        if cur_time is None:
            cur_time = kwargs['time']
        target = self.owner.opponent

        # 确保受到的伤害为层数和伤害之中的最小值
        damage = min(float(self.stack), damage)
        target.hp -= damage
        print(f"[{cur_time:.2f}s] {self.name}被触发，{target.name}受到反伤{damage}点伤害, 当前生命值为{target.hp}/{target.max_hp}")


# 角色类
class Character:
    def __init__(self, index, name, hp=100, stamina=5):
        self._id = index
        self._name = name
        self._max_hp = hp  # 最大生命值
        self._hp = hp  # 当前生命值
        self._base_hp = hp  # 基础生命值
        self._max_stamina = stamina  # 最大耐力值
        self._stamina = stamina  # 当前耐力值
        self._staminaRegen = 1  # 耐力回复，表示1s的回复量
        self._staminaRegen_last_time = 0  # 上一次耐力回复的时间
        self._inventory = []
        self._buffs = BuffPool()
        self._opponent = None

    @property
    def id(self) -> int:
        return self._id

    @property
    def hp(self) -> float:
        return self._hp

    @hp.setter
    def hp(self, value: float) -> None:
        # hp设置的值不能超过最大生命值
        if value > self.max_hp:
            self._hp = self.max_hp
        else:
            self._hp = value

    @property
    def max_hp(self) -> float:
        return self._max_hp

    @max_hp.setter
    def max_hp(self, value: Union[float | int]) -> None:
        # 判断
        if not isinstance(value, (int, float)):
            raise TypeError(f"type value {type(value)} error!")

        if value >= self._max_hp:
            # 默认机制：当最大生命值增加时，当前生命值也会随之增加
            add_hp = value - self._max_hp
            self._max_hp = value
            self.hp += add_hp
        else:
            # 机制：当最大生命值减少时，当前生命值也需要减少
            sub_hp = self._max_hp - value
            self._max_hp = value
            self.hp -= sub_hp

    @property
    def name(self) -> str:
        return self._name

    @property
    def stamina(self) -> float:
        return self._stamina

    @stamina.setter
    def stamina(self, value: float) -> None:
        # 耐力不能小于0
        if value < 0:
            value = 0
        # 耐力不能大于最大耐力值
        if value > self.max_stamina:
            value = self.max_stamina
        self._stamina = value

    @property
    def staminaRegen(self) -> float:
        return self._staminaRegen

    @staminaRegen.setter
    def staminaRegen(self, value: float) -> None:
        self._staminaRegen = value

    @property
    def max_stamina(self) -> float:
        return self._max_stamina

    @property
    def inventory(self) -> list:
        return self._inventory

    @property
    def buffs(self) -> list:
        return self._buffs.buffs

    @property
    def opponent(self) -> 'Character':
        return self._opponent

    def _regen_stamina(self, cur_time: float) -> None:
        self.stamina += (cur_time - self._staminaRegen_last_time) * self._staminaRegen
        self._staminaRegen_last_time = cur_time

    def add_item(self, item: Item):
        print(f"{item.name}加入{self.name}的背包")
        self._inventory.append(item)  # 加入背包
        item.set_owner(self)

        # 注册战斗开始时的事件
        callbacks = item.get_callbacks_by_event_type(EVENT_TYPES['on_start'])
        for callback in callbacks:
            event_system.register(EVENT_TYPES['on_start'], item, callback)
        # 注册受到攻击时的事件
        callbacks = item.get_callbacks_by_event_type(EVENT_TYPES["on_attack"])
        for callback in callbacks:
            event_system.register(EVENT_TYPES["on_attack"], item, callback)

    def add_buff(self, buff: Buff):
        print(f"[{timer.time:.2f}s] {self.name}获得{buff.stack}层{buff.name}")
        buff.set_owner(self)
        self._buffs.add(buff)

        # 如果buff is spike，并且没有该回调
        if isinstance(buff, Spike) and not event_system.has_handler(EVENT_TYPES["on_attack"], buff.trigger):
            event_system.register(EVENT_TYPES["on_attack"], self, buff.trigger)

    def set_opponent(self, character: 'Character') -> None:
        self._opponent = character

    def action(self, cur_time: float) -> None:
        """
        角色基于当前时间，执行一次行动
        :param cur_time:
        :return:
        """

        # 检查背包中的物品是否达到冷却时间
        for item in self.inventory:
            if item.can_use(timer.time) and self.hp > 0:
                item.use(timer.time)

        # 恢复耐力
        self._regen_stamina(cur_time)

        # 检查buff是否达到冷却时间
        for buff in self.buffs:
            if buff.can_trigger(timer.time) and self.hp > 0:
                buff.trigger(timer.time)


# 生成工厂
class CreateFactory:

    @staticmethod
    def create_character(name, *args, **kwargs) -> "Character":
        index = next(id_generator)
        return Character(index, name, *args, **kwargs)

    @staticmethod
    def create_item(name, item_type, *args, **kwargs) -> "Item":
        index = next(id_generator)
        return Item(index, name, item_type, *args, **kwargs)

    @staticmethod
    def create_weapon(name, attack, stamina, *args, **kwargs) -> "Item":
        index = next(id_generator)
        return Weapon(index, name, attack, stamina, *args, **kwargs)

    @staticmethod
    def create_buff(buff_type, *args, **kwargs) -> "Buff":
        if buff_type == BUFF_TYPE["heart"]:
            return Heart(*args, **kwargs)
        elif buff_type == BUFF_TYPE["spike"]:
            return Spike(*args, **kwargs)
        else:
            raise ValueError(f"{buff_type} not in BUFF_TYPE")


class BuffPool:
    def __init__(self):
        self._pool = []  # type: List[Buff]

    @property
    def buffs(self) -> list:
        return self._pool

    def add(self, buff: Buff) -> None:
        buffs = self._get_buffs_by_type(buff.type)
        if buff.type == BUFF_TYPE['heart']:
            # 如果池子里没有对应类型的BUFF
            if len(buffs) == 0:
                # 触发类BUFF，更新使用时间
                buff.last_used = timer.time
                # 添加到buff池中
                self._pool.append(buff)
            # 如果池子里有对应类型的BUFF
            else:
                buffs[0].stack += buff.stack
        elif buff.type == BUFF_TYPE['spike']:
            # 如果池子里没有对应类型的BUFF
            if len(buffs) == 0:
                # 添加到buff池中
                self._pool.append(buff)
            # 如果池子里有对应类型的BUFF
            else:
                buffs[0].stack += buff.stack
        else:
            raise TypeError(f"{buff.type}代码没写")

    def _get_buffs_by_type(self, buff_type: str) -> list:
        return [buff for buff in self._pool if buff.type == buff_type]


# 全局物品池
class GlobalPool:
    def __init__(self):
        self._pool = []

    @property
    def items(self) -> list:
        return self._pool

    def add_item(self, item: 'Item'):
        self._pool.append(item)


def loop_check(player1, player2):

    cur_time = timer.time
    player1.action(cur_time)
    player2.action(cur_time)

    # 额外检查死亡
    if player1.hp <= 0:
        print(f"\n🎉 {player2.name} 获胜！")
        timer.stop()
    if player2.hp <= 0:
        print(f"\n🎉 {player1.name} 获胜！")
        timer.stop()


# 战斗主循环
def battle(player1, player2):
    """战斗开始前"""

    # 设置所有物品的owner/opponent
    for item in player1.inventory:
        item.set_owner(player1)
        item.set_opponent(player2)

    # 设置所有物品的owner/opponent
    for item in player2.inventory:
        item.set_owner(player2)
        item.set_opponent(player1)

    # 设置角色的opponent
    player1.set_opponent(player2)
    player2.set_opponent(player1)

    """战斗开始时"""
    # 触发事件
    event_system.emit(EVENT_TYPES['on_start'])

    timer.add_handler(lambda: loop_check(player1, player2))
    timer.start()
    try:
        while timer.is_running:
            time.sleep(0.01)
    except KeyboardInterrupt:
        timer.stop()


"""回调函数
通用回调参数:
item:       Item            物品
owner:      Character       拥有者
opponent:   Character       对手       
time:       str             时间
"""


# 武器攻击回调函数
def attack_action(damage, *args, **kwargs):
    item = kwargs['item']
    owner = item.owner
    target = item.opponent  # type: Character
    cur_time = kwargs.get('time') or timer.time

    # 需要确保是武器
    if not isinstance(item, Weapon):
        raise TypeError(f"{item.name} is not Weapon")

    target.hp -= damage

    print(
        f"[{cur_time:.2f}s] {item.name}触发，{owner.name}对{target.name}造成了{damage}点伤害，"
        f"{target.name}的生命值为{target.hp}/{target.max_hp}，"
        f"{owner.name}耐力为{owner.stamina:.1f}/{owner.max_stamina}"
    )

    event_system.emit(EVENT_TYPES["on_attack"], damage, *args, **kwargs)


# 生命值增加回调函数
def hp_inc_action(value: float, *args, **kwargs):
    item = kwargs['item']  # type: Item
    owner = item.owner
    cur_time = kwargs['time']
    item.owner.hp += value
    print(
        f"[{cur_time:.2f}s] {item.name}触发，{owner.name}的生命值增加{value}， {owner.name}的生命值为{owner.hp}/{owner.max_hp}")


# 生命值减少回调函数
def hp_dec_action(value: float, *args, **kwargs):
    item = kwargs['item']  # type: Item
    owner = item.owner
    target = item.opponent  # type: Character
    cur_time = kwargs['time']
    target.hp -= value
    print(
        f"[{cur_time:.2f}s] {item.name}触发，{target.name}的生命值减少{value}， {target.name}的生命值为{target.hp}/{target.max_hp}")


# 最大生命值变化回调函数
def max_hp_change_action(value: float, *args, **kwargs):
    item = kwargs['item']  # type: Item
    owner = item.owner
    cur_time = kwargs['time']
    item.owner.max_hp += value
    print(
        f"[{cur_time:.2f}s] {item.name}触发，{owner.name}的最大生命值增加{value}，{owner.name}的生命值为{owner.hp}/{owner.max_hp}")


# 添加BUFF回调函数
def add_buff_action(buff_type: str, num: int, *args, **kwargs):
    item = kwargs['item']  # type: Item
    buff = CreateFactory.create_buff(buff_type, stack=num)
    item.owner.add_buff(buff)


def main():
    # 以下操作其实都是在战场开始前的操作
    # 初始化角色
    player1 = CreateFactory.create_character("战士A", 100, stamina=5)
    player2 = CreateFactory.create_character("战士B", 100, stamina=5)

    # 创建物品
    item1 = CreateFactory.create_item(
        "麻风侏儒",
        ITEM_TYPE["animal"],
        ATTRIBUTE_TYPES["dark"],
        1,
        callbacks={EVENT_TYPES["use"]: [lambda **kwargs: hp_dec_action(5, **kwargs)]},
        text="每x秒造成x点伤害")

    item2 = CreateFactory.create_item(
        "小猪",
        ITEM_TYPE["animal"],
        callbacks={EVENT_TYPES["on_start"]: [lambda **kwargs: max_hp_change_action(2, **kwargs)]},
        text="战斗开始时，增加最大生命值")

    item3 = CreateFactory.create_item(
        "蜗牛壳",
        ITEM_TYPE["animal"],
        cooldown=3,
        callbacks={EVENT_TYPES["use"]: [lambda **kwargs: hp_inc_action(200, **kwargs)]},
        text="x秒后，恢复生命",
        is_use_once=True)

    item4 = CreateFactory.create_item(
        "草药",
        ITEM_TYPE["animal"],
        cooldown=None,
        callbacks={EVENT_TYPES["on_start"]: [lambda **kwargs: add_buff_action(BUFF_TYPE["heart"], 2, **kwargs)]},
        text="战斗开始时，添加两层恢复",
        is_use_once=True)

    item5 = CreateFactory.create_weapon(
        "木剑A",
        2,
        1,
        cooldown=1,
        text="")

    item6 = CreateFactory.create_weapon(
        "木剑B",
        2,
        1,
        cooldown=1.2,
        text="")

    item7 = CreateFactory.create_item(
        "尖刺",
        ITEM_TYPE["none"],
        cooldown=None,
        callbacks={EVENT_TYPES["on_start"]: [lambda **kwargs: add_buff_action(BUFF_TYPE["spike"], 4, **kwargs)]},
        text="战斗开始时，添加一层尖刺",
        is_use_once=True)

    # 添加物品进入战场
    player1.add_item(item1)
    player2.add_item(item2)
    player2.add_item(item3)
    player2.add_item(item4)
    player2.add_item(item5)
    player2.add_item(item6)
    player1.add_item(item7)

    # 启动战斗
    battle(player1, player2)


if __name__ == "__main__":
    main()
